#include "stdafx.h"

#include <tchar.h>
#include <stdlib.h>
#include <stdio.h>

#include "WinThreadsPool.h"

namespace DeepZoomImage
{
	PTP_POOL                        g_pThreadPool = NULL;
	TP_CALLBACK_ENVIRON  g_callbackEnvironment;

	bool createThreadsPool()
	{
		if (g_pThreadPool != NULL) {
			deleteThreadsPool();
		}

		// Initialize the private thread pool
		g_pThreadPool = CreateThreadpool(NULL);
		if (g_pThreadPool == NULL) {
			MessageBoxA( NULL, "Impossible to create private thread pool", "Initialization error", MB_ICONERROR | MB_OK );
			return false;
		}

		// Change  min=2 / max=16 thread pool parameters  max = double of cpu cores
		SetThreadpoolThreadMaximum(g_pThreadPool, 8);
		if (!SetThreadpoolThreadMinimum(g_pThreadPool, 2)) {
			MessageBoxA( NULL,  "SetThreadpoolThreadMinimum failed.", "Initialization error", MB_ICONERROR | MB_OK );
			return false;
		}

		// Reset the callback environment
		// Note: this is an inline function from winbase.h
		//       that calls TpInitializeCallbackEnviron() in winnt.h.
		//       --> 
		//       CallbackEnviron->Version = 1;
		//       CallbackEnviron->Pool = NULL;
		//       CallbackEnviron->CleanupGroup = NULL;
		//       CallbackEnviron->CleanupGroupCancelCallback = NULL;
		//       CallbackEnviron->RaceDll = NULL;
		//       CallbackEnviron->ActivationContext = NULL;
		//       CallbackEnviron->FinalizationCallback = NULL;
		//       CallbackEnviron->u.Flags = 0;
		InitializeThreadpoolEnvironment(&g_callbackEnvironment);

		// Associate our private thread pool with the callback environment
		// Note: this is an inline function from winbase.h
		//       that calls TpSetCallbackThreadpool() in winnt.h.
		//       --> 
		//       CallbackEnviron->Pool = Pool;
		SetThreadpoolCallbackPool(&g_callbackEnvironment, g_pThreadPool);

		return true;
	}

	void deleteThreadsPool()
	{
		// Note: DestroyThreadpoolEnvironment() in winbase.h
		//       calls TpDestroyCallbackEnviron() in winnt.h 
		//       that does... nothing
		//       So, we need to destroy the thread pool ourself
		if (g_pThreadPool != NULL) {
			CloseThreadpool(g_pThreadPool);
		}

		// Clean up callback environment; maybe useful in next version
		// See previous comment
		DestroyThreadpoolEnvironment(&g_callbackEnvironment);
	}

	void threadPoolWorkCallBack(PTP_CALLBACK_INSTANCE Instance, PVOID Context)
	{
		AddMessage( "Thread[%u] [%s] starting for %u seconds.\n",  GetCurrentThreadId(), (char *)Context, 5 );

		// Simulate a lot of work
		Sleep( 5 * 1000);

		AddMessage( "Thread[%u] Instant [%s] is done.\n", GetCurrentThreadId(), (char *)Context );

		delete ((char *)Context);
	}

	void commitWorks()
	{
		for(int current = 1; current <= 40; current++) {
			char *pRequest = new char[MAX_PATH];
			memset( pRequest, 0, MAX_PATH*sizeof(char) );
			sprintf_s( pRequest, MAX_PATH, "Request %u", current );
			if ( TrySubmitThreadpoolCallback( (PTP_SIMPLE_CALLBACK)threadPoolWorkCallBack, pRequest, &g_callbackEnvironment) )
			{
				AddMessage( "Thread[%u] Request %u is submitted.\n", GetCurrentThreadId(), current);
			} else {
				AddMessage( "Thread[%u] Request %u can't be submitted.\n", GetCurrentThreadId(), current);
			}
		}
	}

	void AddMessage( const char * pszFormat, ... )
	{
		char szMsg[1024];
		ZeroMemory( szMsg, sizeof(char)*1024 );

		va_list argList;
		va_start(argList, pszFormat);
		vsprintf(szMsg, pszFormat, argList);
		va_end(argList);

		OutputDebugStringA( szMsg );
	}
}